home *** CD-ROM | disk | FTP | other *** search
/ Aminet 15 / Aminet 15 - Nov 1996.iso / Aminet / dev / lang / FPL_v147.lha / fpl / src / statement.c < prev   
C/C++ Source or Header  |  1996-08-06  |  50KB  |  2,015 lines

  1. /******************************************************************************
  2.  *                   FREXX PROGRAMMING LANGUAGE                  *
  3.  ******************************************************************************
  4.  
  5.  statement.c
  6.  
  7.  Support routines to the Expression() function.
  8.  
  9.  *****************************************************************************/
  10.  
  11. /************************************************************************
  12.  *                                                                      *
  13.  * fpl.library - A shared library interpreting script langauge.         *
  14.  * Copyright (C) 1992-1994 FrexxWare                                    *
  15.  * Author: Daniel Stenberg                                              *
  16.  *                                                                      *
  17.  * This program is free software; you may redistribute for non          *
  18.  * commercial purposes only. Commercial programs must have a written    *
  19.  * permission from the author to use FPL. FPL is *NOT* public domain!   *
  20.  * Any provided source code is only for reference and for assurance     *
  21.  * that users should be able to compile FPL on any operating system     *
  22.  * he/she wants to use it in!                                           *
  23.  *                                                                      *
  24.  * You may not change, resource, patch files or in any way reverse      *
  25.  * engineer anything in the FPL package.                                *
  26.  *                                                                      *
  27.  * This program is distributed in the hope that it will be useful,      *
  28.  * but WITHOUT ANY WARRANTY; without even the implied warranty of       *
  29.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                 *
  30.  *                                                                      *
  31.  * Daniel Stenberg                                                      *
  32.  * Ankdammsgatan 36, 4tr                                                *
  33.  * S-171 43 Solna                                                       *
  34.  * Sweden                                                               *
  35.  *                                                                      *
  36.  * FidoNet 2:201/328    email:dast@sth.frontec.se                       *
  37.  *                                                                      *
  38.  ************************************************************************/
  39.  
  40. #ifdef AMIGA
  41. #include <exec/types.h>
  42. #include <proto/exec.h>
  43. #include <stdlib.h>
  44.  
  45. #include <proto/dos.h>
  46. #include <exec/execbase.h>
  47. #include <dos.h>
  48.  
  49. #include "/funclib/funclib.h"
  50.  
  51. #else
  52. #include <sys/types.h>
  53. #ifdef SUNOS
  54. #include <varargs.h>
  55. #else
  56. #include <stdarg.h>
  57. #endif
  58. #endif
  59.  
  60. #include "script.h"
  61. #include "debug.h"
  62. #include <stdio.h>
  63. #include <stddef.h>
  64. #include <limits.h>
  65.  
  66. static ReturnCode INLINE SendMessage(struct Data *, struct fplMsg *);
  67. static ReturnCode REGARGS Ltostr(struct Data *scr, struct fplStr **,
  68.                      long, long);
  69. static ReturnCode REGARGS GetSymbols(struct Data *, long, long,
  70.                                      struct fplSymbol **);
  71.  
  72. /**********************************************************************
  73.  *
  74.  * ReturnCode CmpAssign()
  75.  *
  76.  * Performs a compound assign to the value the third argument points to.
  77.  * The assign performed is the one with the operator specified in the fourth
  78.  * argument eg, x, +, /, &, %, | etc, etc...
  79.  *
  80.  ***************/
  81.  
  82. ReturnCode REGARGS
  83. CmpAssign(struct Data *scr,
  84.           long val,    /* right operand */
  85.       long *value,    /* return value pointer */
  86.       long flags,    /* variable flags */
  87.       uchar operation)
  88. {
  89.   ReturnCode ret;
  90.   switch(operation) { /* check the type of the assign */
  91.   case CHAR_PLUS:
  92.     *value+=val;
  93.     break;
  94.   case CHAR_MINUS:
  95.     *value-=val;
  96.     break;
  97.   case CHAR_MULTIPLY:
  98.     *value*=val;
  99.     break;
  100.   case CHAR_DIVIDE:
  101.     *value/=val;
  102.     break;
  103.   case CHAR_AND:
  104.     *value&=val;
  105.     break;
  106.   case CHAR_OR:
  107.     *value|=val;
  108.     break;
  109.   case CHAR_REMAIN:
  110.     *value%=val;
  111.     break;
  112.   case CHAR_XOR:
  113.     *value^=val;
  114.     break;
  115.   case CHAR_LESS_THAN:
  116.     *value<<=val;
  117.     break;
  118.   case CHAR_GREATER_THAN:
  119.     *value>>=val;
  120.     break;
  121.   case CHAR_ASSIGN:
  122.     *value=val;
  123.     break;
  124.   default:
  125.     CALL(Warn(scr, FPLERR_ILLEGAL_ASSIGN)); /* >warning< */
  126.     *value=val; /* perform a straight assign! */
  127.     break;
  128.   }
  129.   if(flags&FPL_VARIABLE_LESS32) {
  130.     /* if using less than 32 bit */
  131.     if(flags&FPL_CHAR_VARIABLE)
  132.       *value=(long)((signed char)*value);
  133.     else
  134.       *value=(long)((signed short)*value);
  135.   }
  136.   return(FPL_OK);
  137. }
  138.  
  139.  
  140. /**********************************************************************
  141.  *
  142.  * StrAssign();
  143.  *
  144.  * Assign a string variable.
  145.  *
  146.  ********/
  147.  
  148. ReturnCode REGARGS
  149. StrAssign(struct fplStr *app,
  150.           struct Data *scr,
  151.       struct fplStr **string,
  152.       uchar append) /* TRUE or FALSE if append */
  153. {
  154.   ReturnCode ret;
  155.  
  156.   if(!append) { /* if not append */
  157.     /* Exchange this string with the old one in the variable! */
  158.     if(*string) {
  159.       /*
  160.        * There is a string! Free any type!
  161.        */
  162.       FREE_KIND(*string);
  163.     }
  164.     if(!app) {
  165.       GETMEM(app, sizeof(struct fplStr));
  166.       memset(app, 0, sizeof(struct fplStr)); /* clean it! */
  167.     }
  168.     *string = app;
  169.  
  170.   } else { /* append string */
  171.     if(! (app?app->len:0))
  172.       /* we don't append zero length strings! */
  173.       return FPL_OK;
  174.  
  175.     CALL(AppendStringToString(scr, string, app->string, app->len));
  176.  
  177.   }
  178.   return(FPL_OK);
  179. }
  180.  
  181. /************************************************************************
  182.  *
  183.  * AppendStringToString()
  184.  *
  185.  * Append a generic string to a fplStr.
  186.  *
  187.  */
  188.  
  189. ReturnCode REGARGS
  190. AppendStringToString(struct Data *scr,  /* common data struct */
  191.              struct fplStr **string, /* variable to append to */
  192.              uchar *append, /* string to append */
  193.              long applen)  /* length of append string */
  194. {
  195.     long length;
  196.     long alloc;
  197.     long ln;
  198.     struct fplStr *pek;
  199.     ReturnCode ret;
  200.     void *dest;
  201.     uchar type = *string?TypeMem(*string):MALLOC_DYNAMIC;
  202.  
  203.     length=*string?(*string)->len:0;
  204.     alloc=*string?(*string)->alloc:0;
  205.  
  206.     ln = applen + length; /* total length */
  207.     if (ln>=alloc) { /* do we have that much memory allocated? */
  208.       /*
  209.        * Allocate new memory for string.
  210.        */
  211.  
  212.       GETMEM(pek, sizeof(struct fplStr)+ln+ADDSTRING_INC);
  213.       if(MALLOC_STATIC == type)
  214.         SwapMem(scr, pek, MALLOC_STATIC);
  215.  
  216.       if(*string) {
  217.         memcpy(pek, (*string), length+sizeof(struct fplStr));
  218.         FREE_KIND(*string);
  219.       } else
  220.     pek->len=0;
  221. #ifdef DEBUG
  222.       CheckMem(scr, pek);
  223. #endif
  224.       (*string)=pek;              /* the new pointer */
  225.       (*string)->alloc=ln+ADDSTRING_INC;  /* new allocated size */
  226.     }
  227.  
  228.     dest=(void *)&(*string)->string[length];
  229.  
  230.     /* no string function... only mem-versions! */
  231.     memcpy(dest, (void *)append, applen);
  232.  
  233.     (*string)->len += applen;
  234.  
  235.     (*string)->string[(*string)->len]=CHAR_ASCII_ZERO; /* zero terminate */
  236.  
  237.     return FPL_OK;
  238. }
  239.  
  240.  
  241. /************************************************************************
  242.  *
  243.  * ReturnChar()
  244.  *
  245.  * Returns the ASCII code of the character scr->text points to.
  246.  *
  247.  * Supports 100% ANSI C escape sequences.
  248.  */
  249.  
  250. ReturnCode REGARGS
  251. ReturnChar(struct Data *scr,
  252.            long *num,
  253.            uchar string) /* is this is within quotes */
  254. {
  255.   ReturnCode ret=FPL_OK;
  256.   long cont=TRUE, steps;
  257.   *num=256;
  258.   while(cont) {
  259.     cont=FALSE;
  260.     if(*scr->text==CHAR_BACKSLASH) {
  261.       steps=2;
  262.       switch(scr->text[1]) {
  263.       case CHAR_B:
  264.     *num=CHAR_BACKSPACE;
  265.     break;
  266.       case CHAR_T:
  267.     *num=CHAR_TAB;
  268.     break;
  269.       case CHAR_N:
  270.     *num=CHAR_NEWLINE;
  271.     break;
  272.       case CHAR_F:
  273.     *num=CHAR_FORMFEED;
  274.     break;
  275.       case CHAR_BACKSLASH:
  276.     *num=CHAR_BACKSLASH;
  277.     break;
  278.       case CHAR_QUOTATION_MARK:
  279.     *num=CHAR_QUOTATION_MARK;
  280.     break;
  281.       case CHAR_APOSTROPHE:
  282.     *num=CHAR_APOSTROPHE;
  283.     break;
  284.       case CHAR_A:
  285.     *num=CHAR_ALERT;
  286.     /*   ^^^^ causes warnings ('warning: \a is ANSI C "alert" character')
  287.      * on some compilers. Ignore and look happy!
  288.      */
  289.     break;
  290.       case CHAR_R:
  291.     *num=CHAR_CARRIAGE_RETURN;
  292.     break;
  293.       case CHAR_V:
  294.     *num=CHAR_VERTICAL_TAB;
  295.     break;
  296.       case CHAR_QUESTION:
  297.     *num=CHAR_QUESTION;
  298.     break;
  299.       case CHAR_X:
  300.     steps=*num=0;
  301.     for(scr->text+=2; steps++<2 && isxdigit(*scr->text); scr->text++)
  302.       *num=*num*16+ (isdigit(*scr->text)?
  303.                          *scr->text-CHAR_ZERO:
  304.                          UPPER(*scr->text)-CHAR_UPPER_A+10);
  305.         if(!steps)
  306.       return(FPLERR_SYNTAX_ERROR); /* no number followed \x sequence */
  307.     steps=0;
  308.     break;
  309.       case CHAR_ZERO:
  310.       case CHAR_ONE:
  311.       case CHAR_TWO:
  312.       case CHAR_THREE:
  313.       case CHAR_FOUR:
  314.       case CHAR_FIVE:
  315.       case CHAR_SIX:
  316.       case CHAR_SEVEN:
  317.     *num=steps=0;
  318.     for(scr->text++;steps++<3 && isodigit(*scr->text);)
  319.       *num=*num*8+ *scr->text++ - CHAR_ZERO;
  320.     steps=0;
  321.     break;
  322.       case CHAR_NEWLINE:
  323.     /* After a line continuation backslash, a newline is required!
  324.        This is made to apply to the ANSI C escape sequence standard.
  325.        (added 930113-1305 / DaSt) */
  326.     cont=TRUE;
  327.     scr->virprg++;
  328.     break;
  329.       case CHAR_ASCII_ZERO:
  330.         /* Added 950603 to make the fplConvertString() work better if the
  331.            string to convert ends with a backslash! */
  332.         cont=TRUE;
  333.         CALL(Newline(scr));
  334.         steps=0;
  335.         break;
  336.       default:
  337.     /* Any character not identified as a escape sequence character
  338.        will simply ignore the backslah character!
  339.        (added 930113-1307 / DaSt) */
  340.     *num=scr->text[1];
  341.     break;
  342.       }
  343.       scr->text+=steps;
  344.     } else if(!string && *scr->text== CHAR_NEWLINE) {
  345.       /* This won't occur if the script is preprocessed! */
  346.       cont=TRUE;
  347.       scr->text++;
  348.     } else if(*scr->text== CHAR_ASCII_ZERO) {
  349.       /* This won't occur if the script is preprocessed! */
  350.       cont=TRUE;
  351.       CALL(Newline(scr));
  352.     } else {
  353.       *num=*scr->text;
  354.       scr->text++;
  355.     }
  356.   }
  357.   return(ret);
  358. }
  359.  
  360. /**********************************************************************
  361.  *
  362.  * ReturnCode NewMember(struct Expr **);
  363.  *
  364.  * This function adds a new member in the linked list which keeps
  365.  * track on every operand and it's opertor in the expression.
  366.  *
  367.  *******/
  368.  
  369. ReturnCode REGARGS
  370. NewMember(struct Data *scr,
  371.           struct Expr **expr)
  372. {
  373.   GETMEM((*expr)->next, sizeof(struct Expr));
  374.  
  375.   (*expr)=(*expr)->next;
  376.   (*expr)->val.val=0;
  377.   (*expr)->unary=NULL;
  378.   (*expr)->operator=OP_NOTHING;
  379.   (*expr)->flags=FPL_OPERAND;
  380.   (*expr)->next=NULL;
  381.   return(FPL_OK);
  382. }
  383.  
  384.  
  385. /**********************************************************************
  386.  *
  387.  * ReturnCode Warn();
  388.  *
  389.  * This routines calls the interface function to ask for permission to
  390.  * continue the execution, even though error(s) has/have been found in
  391.  * the interpreted program.
  392.  *
  393.  ******/
  394.  
  395. ReturnCode REGARGS
  396. Warn(struct Data *scr,
  397.      ReturnCode rtrn)
  398. {
  399.   struct fplArgument *pass;
  400.   struct fplMsg *msg;
  401.   ReturnCode ret;
  402.  
  403.   GETMEM(pass, sizeof(struct fplArgument));
  404.   pass->ID=FPL_WARNING;
  405.   pass->key=scr;
  406.   pass->argc=1;
  407.   pass->argv=(void **)&rtrn; /* first ->argv member holds the error/warning number! */
  408.  
  409.   ret=InterfaceCall(scr, pass, scr->function);
  410.  
  411.   FREE(pass);
  412.   GetMessage(scr, FPLMSG_CONFIRM, &msg);
  413.   if(msg) {
  414.     if(msg->message[0]) {
  415.       rtrn=ret;
  416.       scr->prog->warnings++;
  417.     }
  418.     DeleteMessage(scr, msg);
  419.   }
  420.   return(rtrn);
  421. }
  422.  
  423.  
  424. #ifndef AMIGA /* if not using SAS/C on Amiga */
  425.  
  426. #ifdef VARARG_FUNCTIONS
  427. long fplSendTags(void *anchor, ...)
  428. {
  429.   va_list tags;
  430.   long ret;
  431. #ifdef SUNOS
  432.   va_start(tags); /* get parameter list */
  433. #else
  434.   va_start(tags, anchor); /* get parameter list */
  435. #endif
  436.   ret = fplSend(anchor, (unsigned long *)tags);
  437.   va_end(tags);
  438.   return ret;
  439. }
  440. #else
  441. long fplSendTags(void *anchor, unsigned long tags, ...)
  442. {
  443.   return(fplSend(anchor, &tags));
  444. }
  445. #endif
  446.  
  447. #endif
  448.  
  449. /**********************************************************************
  450.  *
  451.  * fplSend()
  452.  *
  453.  * Send a message to FPL.
  454.  *
  455.  ******/
  456.  
  457. ReturnCode PREFIX fplSend(AREG(0) struct Data *scr,
  458.               AREG(1) unsigned long *tags)
  459. {
  460. #ifdef DEBUGMAIL
  461.   DebugMail(scr, MAIL_FUNCTION, 500, "fplSend");
  462. #endif
  463.   return Send(scr, tags);
  464. }
  465.  
  466. ReturnCode REGARGS Send(struct Data *scr,
  467.             unsigned long *tags)
  468. {
  469.   struct fplMsg msg;
  470.   long len=-1;
  471.   struct Program *prog;
  472.   uchar *data=NULL;
  473.   ReturnCode ret;
  474.   struct fplSymbol *symbol;
  475.   struct fplStr *string;
  476.   long mixed;
  477.   static long *resultcode=NULL;
  478.   uchar fplallocstring=FALSE;
  479.   if(!scr)
  480.     return(FPLERR_ILLEGAL_ANCHOR);
  481.  
  482.   memset(&msg, 0, sizeof(struct fplMsg));
  483.  
  484.   while(tags && *tags) {
  485.     switch(*tags++) {
  486.  
  487.     case FPLSEND_STRING:
  488.       /* FPLSEND_PROGRAMFILE is the same tag */
  489.       data=(void *)*tags;
  490.       msg.type=FPLMSG_RETURN;
  491.       msg.flags = FPLMSG_FLG_STRING;
  492.       break;
  493.  
  494.     case FPLSEND_STRLEN:
  495.       len=(long)*tags;
  496.       break;
  497.  
  498.     case FPLSEND_DONTCOPY_STRING: /* the string sent is fplAllocString()'ed */
  499.       fplallocstring=(uchar)*tags;
  500.       break;
  501.  
  502.     case FPLSEND_INT:
  503.       msg.message[0]=(void *)*tags;
  504.       msg.type=FPLMSG_RETURN;
  505.       msg.flags = FPLMSG_FLG_INT;
  506.       break;
  507.  
  508.     case FPLSEND_PROGRAM:
  509.       msg.message[0]=(void *)*tags;
  510.       msg.type=FPLMSG_PROGRAM;
  511.       break;
  512.  
  513.     case FPLSEND_CONFIRM:
  514.       msg.type=FPLMSG_CONFIRM;
  515.       msg.message[0]=(void *)*tags;
  516.       break;
  517.  
  518.     case FPLSEND_GETINTERVAL:
  519.       *(long *)*tags=(long)scr->interfunc;
  520.       break;
  521.  
  522.     case FPLSEND_GETFUNCTION:
  523.       *(long *)*tags=(long)scr->function;
  524.       break;
  525.  
  526.     case FPLSEND_GETLINE:
  527.       *(long *)*tags=scr->prg;
  528.       break;
  529.  
  530.     case FPLSEND_GETVIRFILE:
  531.       *(uchar **)*tags=scr->virfile;
  532.       break;
  533.  
  534.     case FPLSEND_GETVIRLINE:
  535.       *(long *)*tags=scr->virprg;
  536.       break;
  537.  
  538.     case FPLSEND_GETNEWLINE_HOOK:        /* OBSOLETE!!!! */
  539.       break;
  540.  
  541.     case FPLSEND_GETRESULT:
  542.       *(long *)*tags=scr->data;
  543.       break;
  544.  
  545.     case FPLSEND_GETRETURNCODE:
  546.       *(long *)*tags=scr->FPLret;
  547.       break;
  548.  
  549.     case FPLSEND_GETRETURNINT: /* new from V10 */
  550.       *(long **)*tags=scr->returnint;
  551.       break;
  552.  
  553.     case FPLSEND_GETUSERDATA:
  554.       *(long *)*tags=(long)scr->userdata;
  555.       break;
  556.  
  557.     case FPLSEND_GETCOLUMN:
  558.       if(scr->prog && scr->prog->running)
  559.     *(long *)*tags=(scr->text-(&scr->prog->program)[scr->prg-1]+1);
  560.       else if(scr->prog)
  561.     /* we cannot count on this programs presence */
  562.     *(long *)*tags=scr->prog->column;
  563.       else
  564.     *(long *)*tags=0; /* we don't know! */
  565.       break;
  566.  
  567.     case FPLSEND_GETPROGNAME:
  568.       if(scr->prog && scr->prog->name)
  569.     *(uchar **)*tags=scr->prog->name;
  570.       else /* we have no program information */
  571.     *(uchar **)*tags=FPLTEXT_UNKNOWN_PROGRAM;
  572.       break;
  573.  
  574.     case FPLSEND_GETPROG:
  575.       if(scr->prog && scr->prog->program)
  576.     *(uchar **)*tags=scr->prog->program;
  577.       else /* we have no program information */
  578.     *(uchar **)*tags=NULL;
  579.       break;
  580.  
  581.     case FPLSEND_FLUSHCACHE:
  582.       if(*tags)
  583.     FlushFree(scr);
  584.       break;
  585.  
  586.     case FPLSEND_FLUSHFILE:
  587.       if(*tags) {
  588.     prog=scr->programs;
  589.     while(prog) {
  590.       if(prog->name && !strcmp(prog->name, (uchar *)*tags))
  591.         break;
  592.       prog=prog->next;
  593.     }
  594.     if(!prog)
  595.       return(FPLERR_INTERNAL_ERROR);
  596.       } else
  597.     prog=scr->programs;
  598.       while(prog) {
  599.     if(!(prog->running)) {
  600.       /* if the program isn't running right now! */
  601.       len=prog->flags;
  602.       prog->flags&=~PR_CACHEFILE; /* switch off the cache bit now */
  603.       CALL(LeaveProgram(scr, prog));
  604.       prog->flags=len; /* restore flag bits! */
  605.     }
  606.     if(*tags)
  607.       /* only the specified */
  608.       break;
  609.     prog=prog->next;
  610.       }
  611.       break;
  612.  
  613.     case FPLSEND_FREEFILE:
  614.       prog=scr->programs;
  615.       while(prog) {
  616.     if(prog->name && !strcmp(prog->name, (uchar *)*tags))
  617.       break;
  618.     prog=prog->next;
  619.       }
  620.       if(!prog || prog->running || prog->openings)
  621.     /* if not found or if the found one is currently in use! */
  622.     return(FPLERR_ILLEGAL_PARAMETER);
  623.       {
  624.         for(mixed=0; mixed<scr->hash_size; mixed++) {
  625.           register struct Identifier *nident;
  626.           register struct Identifier *ident = scr->hash[mixed];
  627.           while(ident) {
  628.             nident=ident->next;
  629.             if(!strcmp(ident->file, (uchar *)*tags))
  630.               DelIdentifier(scr, NULL, ident);
  631.             ident=nident;
  632.           }
  633.         }
  634.       }
  635.       DelProgram(scr, prog);
  636.       break;
  637.  
  638.     case FPLSEND_STEP:
  639.       if(*tags>0) {
  640.     while((*tags)--) {
  641.       if(!*scr->text)
  642.         CALL(Newline(scr));
  643.       scr->text++;
  644.     }
  645.       } else if((signed int)(*tags)<0) {
  646.     while((*tags)++) {
  647.       if( (scr->text-(&scr->prog->program)[scr->prg-1])>=0)
  648.         scr->text--;
  649.       else
  650.         if(scr->prg>1)
  651.           scr->text=(&scr->prog->program)[--scr->prg-1];
  652.         else
  653.           return(FPLERR_UNEXPECTED_END);
  654.     }
  655.       }
  656.       break;
  657.     case FPLSEND_GETSYMBOL_FUNCTIONS:
  658.       CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION|FPL_INSIDE_FUNCTION,
  659.               FPL_EXPORT_SYMBOL,
  660.               (struct fplSymbol **)*tags));
  661.       break;
  662.     case FPLSEND_GETSYMBOL_MYFUNCTIONS:
  663.       CALL(GetSymbols(scr, FPL_EXTERNAL_FUNCTION, FPL_FUNCTION,
  664.               (struct fplSymbol **)*tags));
  665.       break;
  666.     case FPLSEND_GETSYMBOL_FPLFUNCTIONS:
  667.       CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_INSIDE_FUNCTION,
  668.               (struct fplSymbol **)*tags));
  669.       break;
  670.     case FPLSEND_GETSYMBOL_VARIABLES:
  671.       CALL(GetSymbols(scr, FPL_EXPORT_SYMBOL, FPL_VARIABLE,
  672.               (struct fplSymbol **)*tags));
  673.       break;
  674.     case FPLSEND_GETSYMBOL_ALLVARIABLES:
  675.       CALL(GetSymbols(scr, ~0, FPL_VARIABLE, (struct fplSymbol **)*tags));
  676.       break;
  677.  
  678.     case FPLSEND_GETSYMBOL_ALLFUNCTIONS:
  679.       CALL(GetSymbols(scr, ~0, FPL_FUNCTION, (struct fplSymbol **)*tags));
  680.       break;
  681.  
  682.     case FPLSEND_GETSYMBOL_CACHEDFILES:
  683.       prog=scr->programs;
  684.       mixed=0;
  685.       while(prog) {
  686.     if(prog->flags&PR_CACHEFILE)
  687.       mixed++;
  688.     prog=prog->next;
  689.       }
  690.  
  691.       GETMEM(symbol, sizeof(struct fplSymbol));
  692.       symbol->num=mixed;
  693.       GETMEM(symbol->array, mixed*sizeof(uchar *));
  694.  
  695.       mixed=0;
  696.       prog=scr->programs;
  697.       while(prog) {
  698.     if(prog->flags&PR_CACHEFILE)
  699.       symbol->array[mixed++]=prog->name;
  700.     prog=prog->next;
  701.       }
  702.       *(struct fplSymbol **)*tags=symbol;
  703.  
  704. #ifdef DEBUG
  705.       CheckMem(scr, symbol);
  706.       CheckMem(scr, symbol->array);
  707. #endif
  708.  
  709.       break;
  710.  
  711.       /* ----------------  new from V10: --------------------- */
  712.  
  713.     case FPLSEND_RESULT:
  714.       resultcode = (long *)(*tags); /* long to store result in! */
  715.       break;
  716.  
  717.     case FPLSEND_IS_FILE_CACHED:
  718.       if(resultcode) {
  719.         prog=scr->programs;
  720.         while(prog) {
  721.           if(prog->name && !strcmp(prog->name, (uchar *)(*tags))) {
  722.             *resultcode = TRUE;
  723.             break;
  724.           }
  725.           prog=prog->next;
  726.         }
  727.         *resultcode = FALSE;
  728.       }
  729.       break;
  730.     
  731.       /* --------------------------------------------------- */
  732.  
  733.     case FPLSEND_GETSYMBOL_FREE:
  734. #ifdef DEBUG
  735.       CheckMem(scr, (void *)(*tags));
  736.       CheckMem(scr, ((struct fplSymbol *)*tags)->array);
  737. #endif
  738.       FREE(((struct fplSymbol *)*tags)->array);
  739.       FREE(*tags);
  740.       break;
  741.  
  742. #if defined(AMIGA) && defined(SHARED)
  743.     case FPLSEND_GETSTACKSIZE:
  744.       *(long *)*tags=scr->stack_size;
  745.       break;
  746.     case FPLSEND_GETSTACKUSED:
  747.       *(long *)*tags=GetStackUsed(scr);
  748.       break;
  749. #endif
  750.  
  751.     case FPLSEND_SETPROGNAME:
  752.       if(scr->prog) {
  753.     if(scr->prog->name)
  754.       FREEA(scr->prog->name);
  755.     STRDUPA(scr->prog->name, *tags);
  756.       }
  757.       break;
  758.  
  759.     case FPLSEND_SETFILENAMEGET:
  760.       if(scr->prog) {
  761.     if(*tags)
  762.       scr->prog->flags|=PR_FILENAMEFLUSH;
  763.     else
  764.       scr->prog->flags&=~PR_FILENAMEFLUSH;
  765.       }
  766.       break;
  767.     case FPLSEND_GETVERSION:
  768.       *(long *)(*tags) = FPL_VERSION; /* as defined in FPL.h */
  769.       break;
  770.     case FPLSEND_GETREVISION:
  771.       *(long *)(*tags) = FPL_REVISION; /* as defined in FPL.h */
  772.       break;
  773.     }
  774.     tags++;
  775.   }
  776.   if(!msg.type)
  777.     /*
  778.      * There is no message to send. Everything we had to do is done!
  779.      */
  780.     return(FPL_OK);
  781.  
  782.   if(msg.type==FPLMSG_RETURN && (msg.flags == FPLMSG_FLG_STRING)) {
  783.     if(len<0)
  784.       if(data)
  785.     len=strlen(data);
  786.     if(!len || !data)
  787.       /* this really is a zero length string! */
  788.       msg.message[0]=NULL;
  789.     else {
  790.       if(!fplallocstring) {
  791.         /* we have to duplicate the data */
  792.         GETMEM(msg.message[0], len+sizeof(struct fplStr));
  793.         string=msg.message[0];
  794.         string->len=len;
  795.         string->alloc=len;
  796.         memcpy(string->string, data, len); /* copy string! */
  797.         string->string[string->len]=CHAR_ASCII_ZERO; /* zero terminate */
  798.       } else {
  799.         /* the data was sent as fplAllocString() data! */
  800.         string= (struct fplStr *)(data - offsetof(struct fplStr, string));
  801.         string->len=len;
  802.         string->string[string->len]=CHAR_ASCII_ZERO; /* zero terminate */
  803.         SwapMem(scr, string, MALLOC_DYNAMIC); /* convert */
  804.         msg.message[0]=string;
  805.       }
  806.     }
  807.   }
  808.   CALL(SendMessage(scr, &msg));
  809.   return(ret);
  810. }
  811.  
  812.  
  813. /*********************************************************************
  814.  *
  815.  * fplConvertString()
  816.  *
  817.  * Returns the number of characters converted from the FPL format
  818.  * string to the binary sting stored in a buffer.
  819.  *
  820.  * The output string always get zero terminated!
  821.  *
  822.  *****/
  823.  
  824. long PREFIX
  825. fplConvertString(AREG(0) struct Data *scr,
  826.                  AREG(1) uchar *string,
  827.                  AREG(2) uchar *buffer)
  828. {
  829.   long prg=scr->prg;
  830.   uchar *text=scr->text;
  831.   long line;
  832.   uchar *base;
  833.   long a;
  834.   long number=0;
  835.  
  836. #ifdef DEBUGMAIL
  837.   DebugMail(scr, MAIL_FUNCTION, 500, "fplConvertString");
  838. #endif
  839.  
  840.   if(!scr->prog) {
  841.     /*
  842.      * There is no program at the moment!
  843.      * create a pseudo program for now!
  844.      */
  845.     scr->prog=(struct Program *)MALLOC(sizeof(struct Program));
  846.     if(!scr->prog)
  847.       return(0); /* no characters in output! */
  848.     scr->prog->flags|=PR_TEMPORARY;
  849.   }
  850.   
  851.   base=scr->prog->name;
  852.   line=scr->prog->lines;
  853. /*
  854.   if(*string==CHAR_QUOTATION_MARK)
  855.     string++;
  856. */
  857.  
  858.   scr->prg=1;
  859.   scr->text=string;
  860.   scr->prog->lines=1;
  861.   scr->prog->name=NULL; /* we have no file ID yet! */
  862.  
  863.   while(/* *scr->text!=CHAR_QUOTATION_MARK && */
  864.     !ReturnChar(scr, &a, TRUE)) { /* returns non-zero when an ascii zero is
  865.                          found! */
  866.     *buffer++=a;
  867.     number++;
  868.   }
  869.   
  870.   *buffer=CHAR_ASCII_ZERO;
  871.  
  872.   scr->prg=prg;
  873.   scr->text=text;
  874.   scr->prog->lines=line;
  875.   scr->prog->name=base;
  876.   
  877.   if(scr->prog->flags&PR_TEMPORARY) {
  878.     FREE(scr->prog);
  879.     scr->prog=NULL;
  880.   }
  881.  
  882.   return(number);
  883. }
  884.  
  885. /**********************************************************************
  886.  *
  887.  * GetSymbols();
  888.  *
  889.  * Allocates a structure and data, which is a list of name pointers
  890.  * that match the flag parameter.
  891.  *
  892.  *******/
  893.  
  894. static ReturnCode REGARGS
  895. GetSymbols(struct Data *scr,
  896.            long flag1,
  897.            long flag2,
  898.            struct fplSymbol **get)
  899. {
  900.   long i;
  901.   long num;
  902.   struct Identifier *ident;
  903.   struct fplSymbol *symbol;
  904.  
  905.   for(i=num=0; i<scr->hash_size; i++) {
  906.     ident=scr->hash[i];
  907.     while(ident) {
  908.       if(ident->flags&flag1 && ident->flags&flag2)
  909.     num++;
  910.       ident=ident->next;
  911.     }
  912.   }
  913.  
  914.   GETMEM(symbol, sizeof(struct fplSymbol));
  915.   symbol->num=num;
  916.  
  917.   GETMEM(symbol->array, sizeof(uchar *)*symbol->num);
  918.  
  919.   for(i=num=0; i<scr->hash_size; i++) {
  920.     ident=scr->hash[i];
  921.     while(ident) {
  922.       if(ident->flags&flag1 && ident->flags&flag2)
  923.     symbol->array[num++]=ident->name;
  924.       ident=ident->next;
  925.     }
  926.   }
  927.   *get=symbol;
  928.  
  929. #ifdef DEBUG
  930.   CheckMem(scr, symbol->array);
  931. #endif
  932.   return(FPL_OK);
  933. }
  934.  
  935.  
  936. /**********************************************************************
  937.  *
  938.  * SendMessage();
  939.  *
  940.  * Add a member to the message queue. Allocate a new struct and copy the
  941.  * data of from second parameter message pointer.
  942.  *
  943.  ******/
  944.  
  945. static ReturnCode INLINE
  946. SendMessage(struct Data *scr,
  947.             struct fplMsg *msg)
  948. {
  949.   struct fplMsg *NewMsg, *ptr;
  950.  
  951.   GETMEM(NewMsg, sizeof(struct fplMsg));
  952.  
  953.   *NewMsg=*msg; /* copy all data from source */
  954.  
  955.   /* Queue the message: */
  956.   if(ptr=scr->msg)
  957.     ptr->prev=NewMsg; /* this message becomes the previous for this */
  958.  
  959.   scr->msg=NewMsg;
  960.   NewMsg->next=ptr;
  961.   NewMsg->prev=NULL; /* no previous, this is first! */
  962.  
  963.   return(FPL_OK);
  964. }
  965.  
  966. /**********************************************************************
  967.  *
  968.  * DeleteMessage();
  969.  *
  970.  * Deletes specified or current message (NULL).
  971.  *
  972.  *****/
  973.  
  974. ReturnCode REGARGS
  975. DeleteMessage(struct Data *scr,
  976.               struct fplMsg *msg)
  977. {
  978.   struct fplMsg *ptr=scr->msg;
  979.   if(msg) 
  980.     ptr=msg;
  981.   if(ptr) {
  982.     if(ptr->next)
  983.       ptr->next->prev=ptr->prev; /* redirect next message's prev pointer */
  984.     else if(!ptr->prev) /* is this the only message? */
  985.       scr->msg=NULL;
  986.     if(ptr->prev)
  987.       ptr->prev->next=ptr->next; /* redirect previous message's next pointer */
  988.     FREE(ptr);  /* free message */
  989.   }
  990.   return(FPL_OK);
  991. }
  992.  
  993. /**********************************************************************
  994.  *
  995.  * GetMessage()
  996.  *
  997.  * Returns the first message of the requested type in the pointer
  998.  * in the third argument!
  999.  *
  1000.  ****/
  1001.  
  1002. ReturnCode REGARGS
  1003. GetMessage(struct Data *scr,
  1004.            uchar type,
  1005.            struct fplMsg **get)
  1006. {
  1007.   struct fplMsg *msg=scr->msg;
  1008.   while(*get=msg) {
  1009.     if(msg->type==type)
  1010.       break;
  1011.     msg=msg->next;
  1012.   }
  1013.   return(FPL_OK);
  1014. }
  1015.  
  1016. /**********************************************************************
  1017.  *
  1018.  * GetProgram();
  1019.  *
  1020.  * Whenever we want to access a program in the program list, we do it
  1021.  * using this function. This enables heavy program swapping capabilities.
  1022.  * Programs that are not being used can be flushed from memory and brought
  1023.  * back whenever we need it!
  1024.  *
  1025.  ******/
  1026.  
  1027. ReturnCode REGARGS
  1028. GetProgram(struct Data *scr,
  1029.            struct Program *prog)
  1030. {
  1031.   struct fplArgument *arg;
  1032.   ReturnCode ret;
  1033.   struct fplMsg *msg;
  1034.   struct fplStr *string;
  1035.   if(!prog->program) {
  1036.     /*
  1037.      * The program is not currently in memory. Get it!
  1038.      */
  1039.     
  1040.     if(prog->flags&PR_FILENAMEFLUSH) {
  1041.       /*
  1042.        * We know that the program is simply to load from the file the program
  1043.        * name specifies.
  1044.        */
  1045.       CALL(ReadFile(scr, prog->name, prog));
  1046.     } else {
  1047.       /*
  1048.        * We must ask user for information!
  1049.        */
  1050.       
  1051.       GETMEM(arg, sizeof(struct fplArgument));
  1052.       arg->ID=FPL_FILE_REQUEST;
  1053.       arg->key=(void *)scr;
  1054.       arg->argv=(void **)&prog->name;
  1055.       arg->argc=1;
  1056.       ret=InterfaceCall(scr, arg, scr->function);
  1057.       FREE(arg);
  1058.       if(ret)
  1059.         return ret;
  1060.       
  1061.       GetMessage(scr, FPLMSG_PROGRAM, &msg);
  1062.       if(!msg) {
  1063.     GetMessage(scr, FPLMSG_RETURN, &msg);
  1064.     if(!msg || ((msg->flags&FPLMSG_FLG_BITS) != FPLMSG_FLG_STRING))
  1065.       /*
  1066.        * No kind of proper answer could be found!
  1067.        * Dead end failure!
  1068.        */      
  1069.       return FPLERR_INTERNAL_ERROR;
  1070.     
  1071.     string=(struct fplStr *)msg->message[0];
  1072.     ret = ReadFile(scr, string->string, prog);
  1073.     FREE(msg->message[0]); /* we don't need this anymore! */
  1074.     if(ret)
  1075.       return ret;
  1076.       } else {
  1077.     /*
  1078.      * User supplied us with a memory pointer to the program again!
  1079.      */
  1080.     prog->program= (uchar *)msg->message[0];
  1081.     prog->flags|=PR_USERSUPPLIED;
  1082.       }
  1083.       DeleteMessage(scr, msg);
  1084.     }
  1085.   } /* else
  1086.        we already have it loaded! */
  1087.   prog->running++;
  1088.   return(FPL_OK);
  1089. }
  1090.  
  1091.  
  1092. /**********************************************************************
  1093.  *
  1094.  * LeaveProgram();
  1095.  *
  1096.  * If we leave one program, call this. If any flush is to be done, this
  1097.  * will perform that!
  1098.  *
  1099.  ******/
  1100.  
  1101. ReturnCode REGARGS
  1102. LeaveProgram(struct Data *scr,
  1103.              struct Program *prog)
  1104. {
  1105.   struct fplArgument *arg;
  1106.   ReturnCode ret;
  1107.   struct fplMsg *msg;
  1108.   prog->running--;
  1109.   if(prog->program && !prog->running && prog->flags&PR_FLUSH_NOT_IN_USE) {
  1110.     /*
  1111.      * The program is there and no one is using it!
  1112.      * flush it if we want to!
  1113.      */
  1114.  
  1115.     if(prog->flags&PR_USERSUPPLIED) {
  1116.       /*
  1117.        * This program is supplied by the external program. We cannot
  1118.        * free the memory, only tell our father that freeing is OK...
  1119.        */
  1120.       GETMEM(arg, sizeof(struct fplArgument));
  1121.       arg->ID=FPL_FLUSH_FILE;
  1122.       arg->key=(void *)scr;
  1123.       arg->argv=(void **)&prog->name;
  1124.       arg->argc=1;
  1125.       CALL(InterfaceCall(scr, arg, scr->function));
  1126.       FREE(arg);
  1127.       GetMessage(scr, FPLMSG_CONFIRM, &msg);
  1128.       /*
  1129.        * We require a {FPLSEND_CONFIRM, TRUE} message from the user before we
  1130.        * flush the user supplied function! Simply ignore implementing any
  1131.        * answer to this message if we never want to flush user supplied
  1132.        * functions.
  1133.        */
  1134.       if(msg && msg->message[0])
  1135.     /* If we got a "OK" message! */
  1136.     prog->program=NULL;
  1137.       if(msg)
  1138.     DeleteMessage(scr, msg);
  1139.     } else {
  1140.       /*
  1141.        * The memory occupied by this program is our business.
  1142.        * Swap the memory first to be sure we know the kind of it!
  1143.        */
  1144.       SwapMem(scr, prog->program, MALLOC_DYNAMIC);
  1145.       FREE(prog->program);
  1146.       prog->program=NULL; /* to visualize the clearing of this program! */
  1147.     }
  1148.   }
  1149.   return(FPL_OK);
  1150. }
  1151.  
  1152. static long INLINE
  1153. Exists(struct Data *scr,
  1154.        uchar *name,
  1155.        long check)
  1156. {
  1157.   ReturnCode ret;
  1158.   struct Identifier *ident;
  1159.   GetIdentifier(scr, name, &ident);
  1160.   if(check && ident) {
  1161.     switch(tolower(check)) {
  1162.     case EXISTS_FUNCTION:
  1163.       ret = ident->flags&FPL_FUNCTION;
  1164.       break;
  1165.     case EXISTS_INTEGER:
  1166.       ret = ident->flags&FPL_INT_VARIABLE;
  1167.       break;
  1168.     case EXISTS_STRING:
  1169.       ret = ident->flags&FPL_STRING_VARIABLE;
  1170.       break;
  1171.     case EXISTS_VARIABLE:
  1172.       ret = ident->flags&FPL_VARIABLE;
  1173.       break;
  1174.     }
  1175.   }
  1176.   else
  1177.     ret = ident?TRUE:FALSE;
  1178.   return ret;
  1179. }
  1180.  
  1181. /**********************************************************************
  1182.  *
  1183.  * int functions(struct fplArgument *);
  1184.  *
  1185.  * This function handles the internal functions. *EXACTLY* the same way
  1186.  * external processes handles their functions!!! :-)
  1187.  *
  1188.  *****/
  1189.  
  1190. ReturnCode REGARGS
  1191. functions(struct fplArgument *arg)
  1192. {
  1193.   struct Expr *val;
  1194.   unsigned long inttags[]={FPLSEND_INT, 0, FPLSEND_DONE};
  1195.   unsigned long strtags[]={FPLSEND_STRING, 0, FPLSEND_STRLEN, 0, FPLSEND_DONE};
  1196.   long base;
  1197.   ReturnCode ret;
  1198.   struct Data *scr=(struct Data *)arg->key;
  1199.   struct fplStr *string;
  1200.   long prg;
  1201.   long line;
  1202.   long virprg;
  1203.   uchar *virfile;
  1204.   uchar *text;
  1205. /*  uchar *file; */
  1206.   long len;        /* length of the string */
  1207.   register long col;    /* the column parameter */
  1208.   switch(arg->ID) {
  1209.     
  1210.   case FNC_ABS:
  1211.     inttags[1]= ABS((long)arg->argv[0]);
  1212.     CALL(Send(arg->key, inttags));
  1213.     break;
  1214.  
  1215.   case FNC_ITOC:
  1216.     prg=(long)arg->argv[0]&255;
  1217.     text=(uchar *)&line; /* we just need 2 bytes to play with in peace! */
  1218.     text[1]='\0';
  1219.     text[0]=prg;
  1220.     strtags[1]=(long)text;
  1221.     strtags[3]=1;
  1222.     CALL(Send(scr, strtags));
  1223.     break;
  1224.     
  1225.   case FNC_JOINSTR:
  1226.     string=NULL;
  1227.     for(prg=0; prg<arg->argc; prg++) {
  1228.       CALL(StrAssign((struct fplStr *) ((uchar *)arg->argv[prg]-
  1229.                     offsetof(struct fplStr, string)),
  1230.              scr, &string, TRUE));
  1231.     }
  1232.     if(string) {
  1233.       strtags[1]=(unsigned long)string->string;
  1234.       strtags[3]=string->len;
  1235.       CALL(Send(scr, strtags));
  1236.       FREE(string);
  1237.     }
  1238.     break;
  1239.  
  1240.   case FNC_ITOA:
  1241.   case FNC_LTOSTR:
  1242.     base=(arg->argc<2?10:(long)arg->argv[1]);
  1243.     CALL(Ltostr(scr, &string, base, (long)arg->argv[0]));
  1244.     strtags[1]=(unsigned long)string->string;
  1245.     strtags[3]=string->len;
  1246.     CALL(Send(scr, strtags));
  1247.     FREE(string);
  1248.     break;
  1249.     
  1250.   case FNC_ATOI:
  1251.   case FNC_STRTOL:
  1252.     base=(arg->argc<2?10:(long)arg->argv[1]);
  1253.     inttags[1]= Strtol((uchar *)arg->argv[0], base, &text);
  1254.     CALL(Send(scr, inttags));
  1255.     break;
  1256.     
  1257.   case FNC_EVAL:
  1258.     prg=scr->prg;
  1259.     text=scr->text;
  1260.     line=scr->prog->lines;
  1261.     virprg=scr->virprg;
  1262.     virfile=scr->virfile;
  1263.  
  1264.     scr->virprg=1;
  1265.     scr->virfile=NULL;
  1266.     scr->text=(uchar *)arg->argv[0];
  1267.     scr->prg=scr->prog->lines=1;
  1268.  
  1269.     GETMEM(val, sizeof(struct Expr));
  1270.     CALL(Expression(val, scr, CON_GROUNDLVL|CON_END|CON_NUM, NULL));
  1271.     inttags[1]=val->val.val;
  1272.     FREE(val);
  1273.  
  1274.     scr->prg=prg;
  1275.     scr->text=text;
  1276.     scr->prog->lines=line;
  1277.     scr->virprg=virprg;
  1278.     scr->virfile=virfile;
  1279.     
  1280.     CALL(Send(scr, inttags));
  1281.     break;
  1282.     
  1283.   case FNC_INTERPRET:
  1284.     prg=scr->prg;
  1285.     text=scr->text;
  1286.     line=scr->prog->lines;
  1287. /*    file=scr->prog->name; */
  1288.     virprg=scr->virprg;
  1289.     virfile=scr->virfile;
  1290.     scr->virprg=1;
  1291.     scr->virfile=NULL;
  1292.     scr->interpret=NULL; /* nothing recursive here, no no! */
  1293.     scr->prg=1;
  1294.     scr->text=(uchar *)arg->argv[0];
  1295.     scr->prog->lines=1;
  1296. /*    scr->prog->name=NULL; */ /* we have no file name! */
  1297.  
  1298.     GETMEM(val, sizeof(struct Expr));
  1299.     ret=Script(scr, val, SCR_NORMAL, NULL);
  1300.     inttags[1]=val->val.val;
  1301.     FREE(val);
  1302.  
  1303.     if(ret) {
  1304.       /*
  1305.        * Check if the error occurred somewhere in the real program
  1306.        * or if it was within the argument. If within argument, we
  1307.        * set back the previous program pointer, otherwise not.
  1308.        */
  1309.       for(base=0;base<line;base++)
  1310.     if(scr->text>(&scr->prog->program)[base] &&
  1311.        scr->text<((&scr->prog->program)[base]+
  1312.               strlen((&scr->prog->program)[base])))
  1313.       break;
  1314.       if(base==line) {
  1315.     scr->prg=prg;
  1316.     scr->text=text;
  1317.     scr->prog->lines=line;
  1318. /*    scr->prog->name=file; */
  1319.       }
  1320.       return(ret);
  1321.     }
  1322.     scr->prg=prg;
  1323.     scr->text=text;
  1324.     scr->prog->lines=line;
  1325. /*    scr->prog->name=file; */
  1326.     scr->virprg=virprg;
  1327.     scr->virfile=virfile;
  1328.     CALL(Send(arg->key, inttags));
  1329.     break;
  1330.     
  1331.   case FNC_RENAME:
  1332.     {
  1333.       struct Identifier *ident;
  1334.       GetIdentifier(scr, (uchar *)arg->argv[0], &ident);
  1335.       if(!ident || ident->flags&FPL_KEYWORD) {
  1336.         /* not found or found keyword, fail! */
  1337.         inttags[1] = FPLERR_IDENTIFIER_NOT_FOUND;
  1338.       }
  1339.       else {
  1340.         if(((char *)arg->argv[1])[0])
  1341.           inttags[1] = RenameIdentifier(scr, ident, arg->argv[1]);
  1342.         else
  1343.           inttags[1] = DelIdentifier(scr, NULL, ident);
  1344.       }
  1345.       CALL(Send(arg->key, inttags));
  1346.     }    
  1347.   break;
  1348.  
  1349.   case FNC_STRCMP:
  1350.   case FNC_STRICMP:
  1351.     /*
  1352.      * strcmp() with strings that can include a zero byte must use
  1353.      * memcmp(), but that also takes a third length argument which
  1354.      * must never be larger than the smallest of the two compared
  1355.      * strings!
  1356.      */
  1357.     line = MIN(FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)); /* len */
  1358.  
  1359.     if(FNC_STRCMP == arg->ID)
  1360.       base = memcmp(arg->argv[0], arg->argv[1], line);
  1361.     else
  1362.       base = my_memicmp(arg->argv[0], arg->argv[1], line);
  1363.  
  1364.     if(!base && FPL_STRING_LENGTH(arg, 0) != FPL_STRING_LENGTH(arg, 1)) {
  1365.       /* similar strings after 'line' characters */
  1366.  
  1367.       /*
  1368.        * The strings are of different length.
  1369.        */
  1370.  
  1371.       base = ((uchar *)arg->argv[0])[line] -
  1372.     (FPL_STRING_LENGTH(arg, 1)>line?
  1373.      ((uchar *)arg->argv[1])[line] : 0 );
  1374.       
  1375.       if(!base) {
  1376.     /* only possible since FPL strings can hold zeroes! */
  1377.     base = 256; /* not possible in regular C */
  1378.       }
  1379.     }
  1380.     inttags[1]=base;
  1381.     CALL(Send(scr, inttags));
  1382.     break;
  1383.     
  1384.   case FNC_SUBSTR:
  1385.     len=FPL_STRING_LENGTH(arg, 0);
  1386.     col=(long)arg->argv[1];
  1387.     if(col>len || col<0) {
  1388.       ;                /* we can't get any string! */
  1389.     } else {
  1390.       len-=col;            /* Maximum length we can get */
  1391.       strtags[3]=((long)arg->argv[2]>len?len:(long)arg->argv[2]); /* strlen */
  1392.       strtags[1]=(long) arg->argv[0]+col; /* return string from here */
  1393.       CALL(Send(scr, strtags));
  1394.     }
  1395.     break;
  1396.     
  1397.   case FNC_STRLEN:
  1398.     inttags[1]=FPL_STRING_LENGTH(arg, 0);
  1399.     CALL(Send(scr, inttags));
  1400.     break;
  1401.  
  1402.   case FNC_STRNCMP:
  1403.   case FNC_STRNICMP:
  1404.     /*
  1405.      * strncmp() with strings that can include a zero byte must use
  1406.      * memcmp(), that also takes a third length argument which
  1407.      * must never be larger than the smallest of the two compared
  1408.      * strings or the number specified!
  1409.      */
  1410.     if(FNC_STRNCMP == arg->ID) {
  1411.       inttags[1]=
  1412.     memcmp(arg->argv[0], arg->argv[1],
  1413.            MIN3((long)arg->argv[2],
  1414.             FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)));
  1415.     }
  1416.     else {
  1417.       inttags[1]=
  1418.     my_memicmp(arg->argv[0], arg->argv[1],
  1419.         MIN3((long)arg->argv[2],
  1420.              FPL_STRING_LENGTH(arg, 0), FPL_STRING_LENGTH(arg, 1)));
  1421.     }
  1422.     CALL(Send(scr, inttags));
  1423.     break;
  1424.     
  1425.   case FNC_STRSTR:
  1426.   case FNC_STRISTR:
  1427.     /*
  1428.      * strstr() should compare two memory regions, like a memmem()!
  1429.      * Code an own!
  1430.      */
  1431.     base = FPL_STRLEN(arg->argv[0]);
  1432.     line = FPL_STRLEN(arg->argv[1]);
  1433.     text = (uchar *)arg->argv[0];
  1434.  
  1435.     /*
  1436.      * Addition from FPL version 9:
  1437.      * starting search column in third parameter!
  1438.      */
  1439.     if(arg->argc>2) {
  1440.       if((int)arg->argv[2] < base) {
  1441.     text+=(int)arg->argv[2];
  1442.     base-=(int)arg->argv[2];
  1443.       }
  1444.       else {
  1445.     /* tried to start searching outside the string! */
  1446.  
  1447.     line = 1; /* to make a not-found return code */
  1448.     base = 0;
  1449.       }
  1450.     }
  1451.     
  1452.     if(line && base) {
  1453.       if(FNC_STRSTR == arg->ID) {
  1454.     /* Case sensitive */
  1455.     while(base-->=line) {
  1456.       if(!memcmp(text, (uchar *)arg->argv[1], line)) {
  1457.         line=0;
  1458.         break;
  1459.       }
  1460.       text++;
  1461.     }
  1462.       }
  1463.       else {
  1464.     /* Case insensitive */
  1465.     while(base-->=line) {
  1466.       if(!my_memicmp(text, (uchar *)arg->argv[1], line)) {
  1467.         line=0;
  1468.         break;
  1469.       }
  1470.       text++;
  1471.     }
  1472.       }
  1473.     }
  1474.     inttags[1]=line?-1:text-(uchar *)arg->argv[0];
  1475.  
  1476.     /* OLD ONE:
  1477.     text=(uchar *)strstr((uchar *)arg->argv[0], (uchar *)arg->argv[1]);
  1478.     inttags[1]=text?text-(uchar *)arg->argv[0]:-1;
  1479.     */
  1480.     CALL(Send(scr, inttags));
  1481.     break;
  1482.  
  1483.   case FNC_SPRINTF:
  1484.     {
  1485.         static unsigned long tags[]={
  1486.             FPLSEND_STRING, 0,
  1487.             FPLSEND_STRLEN, 0,
  1488.             FPLSEND_DONTCOPY_STRING, TRUE,
  1489.             FPLSEND_DONE
  1490.         };
  1491.         string = NULL;
  1492.         CALL(Sprintf(scr, &string, arg->argv[0], arg->argv, arg->format,
  1493.              arg->argc));
  1494.         if(string) {
  1495.           tags[1] = (long)string->string;
  1496.           tags[3] = string->len;
  1497.           CALL(Send(scr, tags));
  1498.         }
  1499.     }
  1500.     break;
  1501.  
  1502.   case FNC_SSCANF:
  1503.     {
  1504.       inttags[1] =
  1505.       Sscanf(scr,
  1506.          (uchar *)arg->argv[0],
  1507.          (uchar *)arg->argv[1],
  1508.          arg->argc,
  1509.          arg->argv,
  1510.          arg->format);
  1511.       CALL(Send(scr, inttags));
  1512.     }
  1513.     break;
  1514.   case FNC_EXISTS:
  1515.     inttags[1] =  Exists(scr,
  1516.              (uchar *)arg->argv[0],
  1517.              arg->argc>1?(long)arg->argv[1]:0);
  1518.     CALL( Send(scr, inttags) );
  1519.     break;
  1520.  
  1521. #if defined(AMIGA)
  1522.   case FNC_OPENLIB:
  1523.     CALL(OpenLib(scr,
  1524.                  (uchar *)arg->argv[0], /* name */
  1525.                  (long)arg->argv[1],   /* version */
  1526.                  (long *)&inttags[1],  /* funclib result */
  1527.                  0));                  /* normal 'soft' open */
  1528.     CALL(Send(scr, inttags));
  1529.     break;
  1530.  
  1531.   case FNC_CLOSELIB:
  1532.     CALL(CloseLib(scr,
  1533.                   (uchar *)arg->argv[0],  /* name */
  1534.                   0,                     /* 'soft' close */
  1535.                   (long *)&inttags[1])); /* funclib result */
  1536.     CALL(Send(scr, inttags));
  1537.     break;
  1538.  
  1539.   case FNC_DEBUG:
  1540.     if(arg->argc) {
  1541.       if(!(int)arg->argv[0]) {
  1542.         scr->flags&=~FPLDATA_DEBUG_MODE; /* switch off debug mode */
  1543. #ifdef DEBUGMAIL
  1544.         DebugMail(scr, MAIL_EXIT, 500, NULL);
  1545. #endif
  1546.       }
  1547.       else {
  1548.         scr->flags|=FPLDATA_DEBUG_MODE;  /* switch on debug mode */
  1549. #ifdef DEBUGMAIL
  1550.         DebugMail(scr, MAIL_START, 500, NULL);
  1551. #endif
  1552.       }
  1553.     }
  1554.     else {
  1555.        inttags[1]=scr->flags&FPLDATA_DEBUG_MODE?1:0; /* return status */
  1556.        CALL(Send(scr, inttags));
  1557.     }
  1558.     break;
  1559. #endif
  1560.   }
  1561.   return(FPL_OK);
  1562. }
  1563.  
  1564. #if defined(AMIGA)
  1565. ReturnCode REGARGS
  1566. OpenLib(struct Data *scr,
  1567.         uchar *lib,        /* funclib name */
  1568.         long version,     /* funclib version */
  1569.         long *retvalue,   /* funclib return code */
  1570.         uchar flags)
  1571. {
  1572.    struct MyLibrary *library;
  1573.    struct Library *DOSBase;
  1574.    BPTR seglist;
  1575.    uchar *command;
  1576.    uchar *cmd;
  1577.    struct FuncList *namelist=scr->funclibs;
  1578.    uchar *name;
  1579.    ReturnCode ret;
  1580.    struct fplStr *string;
  1581.  
  1582.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  1583.  
  1584.    library = (struct MyLibrary *)getreg(REG_A6);
  1585.    DOSBase = library->ml_DosBase;
  1586.  
  1587.    GETMEM(command, FPLLIB_MAXSPACE);
  1588.  
  1589.    while(namelist) {
  1590.      if(!strcmp(namelist->name, lib)) {
  1591.        namelist->opens++;
  1592.        return FPL_OK; /* this funclib is already opened */
  1593.      }
  1594.      namelist = namelist->next;
  1595.    }
  1596.  
  1597.    cmd = command;
  1598.    strcpy(command, FPLLIB_SOURCE);
  1599.    strcpy(command+strlen(FPLLIB_SOURCE), lib);
  1600.    seglist = LoadSeg(command); /* load the command! */
  1601.    if(seglist) {
  1602.      strcpy(command, FPLLIB_OPENCMD);
  1603.      command += strlen(FPLLIB_OPENCMD);
  1604.  
  1605.      CALL(Ltostr(scr, &string, 10, (long)scr));
  1606.      strcpy(command, string->string);
  1607.      command[string->len]= ' '; /* pad with a single space */
  1608.      command+=string->len+1;
  1609.      FREE(string);
  1610.  
  1611.      CALL(Ltostr(scr, &string, 10, version));
  1612.      strcpy(command, string->string);
  1613.      command[string->len]= '\n';   /* add newline */
  1614.      command[string->len+1]= '\0'; /* zero terminate */
  1615.      FREE(string);
  1616.  
  1617.      if(SysBase->SoftVer<36) {
  1618.        /* V33 solution! */
  1619.        uchar *segment = BADDR(seglist);
  1620.        int (*func)();
  1621. #pragma msg 147 ignore
  1622.        func = segment + 4; /* generates warning */
  1623. #pragma msg 147 warning
  1624.  
  1625.        putreg(REG_A0, (long)cmd);
  1626.        putreg(REG_D0, strlen(cmd));
  1627. #pragma msg 154 ignore
  1628.        *retvalue = (func)(); /* generates warning */
  1629. #pragma msg 154 warning
  1630.      } else /* version 36 or up! */
  1631.        *retvalue = RunCommand(seglist, 4000, cmd, strlen(cmd));
  1632.  
  1633.      UnLoadSeg( seglist );
  1634.    } else {
  1635.      /* we failed loading the command! */
  1636.      *retvalue = FUNCLIB_LOAD;
  1637.    }
  1638.  
  1639.    FREE(cmd);
  1640.  
  1641.    if(!*retvalue) {
  1642.       GETMEMA(namelist, sizeof(struct FuncList));
  1643.       STRDUPA(name, lib);
  1644.       namelist->name = name;
  1645.       namelist->opens = 1;
  1646.       namelist->flags = flags;
  1647.       namelist->next = scr->funclibs;
  1648.       scr->funclibs = namelist;
  1649.    }
  1650.    return FPL_OK;
  1651. }
  1652.  
  1653. ReturnCode REGARGS
  1654. CloseLib(struct Data *scr,
  1655.          uchar *lib,        /* funclib name or NULL for all */
  1656.          long flags,       /* options */
  1657.          long *retvalue)   /* funclib return code */
  1658. {
  1659.    struct MyLibrary *library;
  1660.    struct Library *DOSBase;
  1661.    struct FuncList *namelist=scr->funclibs;
  1662.    struct FuncList *prevlist=NULL;
  1663.    struct FuncList *next;
  1664.    uchar *command;
  1665.    uchar *cmd;
  1666.    ReturnCode ret;
  1667.    struct fplStr *string;
  1668.    BPTR seglist;
  1669.  
  1670.    struct ExecBase *SysBase = *(struct ExecBase **)4;
  1671.  
  1672.    library = (struct MyLibrary *)getreg(REG_A6);
  1673.    DOSBase = library->ml_DosBase;
  1674.  
  1675.    GETMEM(command, FPLLIB_MAXSPACE);
  1676.    cmd = command;
  1677.  
  1678.    while(namelist) {
  1679.      if(namelist->flags&FPLLIB_KEEP && namelist->opens==1) {
  1680.        /* This funclib is prevented from being 'soft' closed! */
  1681.        namelist->opens++;
  1682.      }
  1683.      if((!lib || !strcmp(namelist->name, lib)) &&
  1684.         (!--namelist->opens || flags&FPLLIB_FORCE) ) {
  1685.        /* the funclib _is_ opened! */
  1686.  
  1687.        strcpy(command, FPLLIB_SOURCE);
  1688.        strcpy(command+strlen(FPLLIB_SOURCE), lib);
  1689.        seglist = LoadSeg(command); /* load the command! */
  1690.        if(seglist) {
  1691.          strcpy(command, FPLLIB_CLOSECMD);
  1692.          command += strlen(FPLLIB_CLOSECMD);
  1693.     
  1694.          CALL(Ltostr(scr, &string, 10, (long)scr));
  1695.          strcpy(command, string->string);
  1696.          command[string->len]= '\n';   /* add newline */
  1697.          command[string->len+1]= '\0'; /* zero terminate */
  1698.          FREE(string);
  1699.     
  1700.          if(SysBase->SoftVer<36) {
  1701.            /* V33 solution! */
  1702.            uchar *segment = BADDR(seglist);
  1703.            int (*func)();
  1704. #pragma msg 147 ignore
  1705.            func = segment + 4; /* generates warning */
  1706. #pragma msg 147 warning
  1707.     
  1708.            putreg(REG_A0, (long)cmd);
  1709.            putreg(REG_D0, strlen(cmd));
  1710. #pragma msg 154 ignore
  1711.            *retvalue = (func)(); /* generates warning */
  1712. #pragma msg 154 warning
  1713.          } else /* version 36 or up! */
  1714.            *retvalue = RunCommand(seglist, 4000, cmd, strlen(cmd));
  1715.     
  1716.          UnLoadSeg( seglist );
  1717.        } else {
  1718.          /* we failed loading the command! */
  1719.          *retvalue = FUNCLIB_LOAD;
  1720.        }
  1721.     
  1722.     
  1723.        if(!*retvalue) {
  1724.          next = namelist->next;
  1725.          if(prevlist) /* was there a previous funclib in the list? */
  1726.            prevlist->next=next; /* point it to the next in the list */
  1727.          else
  1728.            scr->funclibs = next; /* point the origin to the next */
  1729.          FREEA(namelist->name); /* free name space */
  1730.          FREEA(namelist);       /* free struct */
  1731.          namelist = next;
  1732.          continue;
  1733.        }
  1734.      }
  1735.      prevlist = namelist;
  1736.      namelist = namelist->next;
  1737.    }
  1738.  
  1739.    FREE(cmd);
  1740.  
  1741.    return FPL_OK;
  1742. }
  1743.  
  1744. #endif
  1745.  
  1746. /**********************************************************************
  1747.  *
  1748.  * fplLtostr()
  1749.  *
  1750.  * Frontend to the FPL Ltostr() function to be used by anyone! The returned
  1751.  * string *must* be freed using the fplFreeString() function!
  1752.  *
  1753.  ****/
  1754.  
  1755. uchar * PREFIX
  1756. fplLtostr(AREG(0) struct Data *scr,
  1757.           DREG(0) long base,
  1758.           DREG(1) long num)
  1759. {
  1760.   ReturnCode ret;
  1761.   struct fplStr *string;
  1762.   uchar *retstring=NULL;
  1763. #ifdef DEBUGMAIL
  1764.   DebugMail(scr, MAIL_FUNCTION, 500, (void *)"fplLtostr");
  1765. #endif
  1766.   ret = Ltostr(scr, &string, base, num);
  1767.   if(FPL_OK == ret) {
  1768.     SwapMem(scr, string, MALLOC_STATIC); /* turn allocation to static type */
  1769.     retstring = string->string; /* return string pointer */
  1770.   }
  1771.   return retstring;
  1772. }
  1773.  
  1774.  
  1775. static ReturnCode REGARGS
  1776. Ltostr(struct Data *scr,
  1777.        struct fplStr **string,
  1778.        long base,
  1779.        long num)
  1780. {
  1781.   /*
  1782.    * Convert the integer to string with `any base'-convertions.
  1783.    */
  1784.   extern const uchar upper_digits[];
  1785.   ReturnCode ret;
  1786.   static const uchar *digits = upper_digits;
  1787.   long is_neg=num<0;
  1788.   long len=0;
  1789.   uchar buffer[34+sizeof(struct fplStr)];
  1790.   uchar *bufpoint;  /* the accurate position in the buffer */
  1791.  
  1792.   if(base>strlen(digits)) {
  1793.     CALL(Warn(scr, FPLERR_OUT_OF_REACH));
  1794.     num=strlen(digits); /* reset to maximum */
  1795.   }
  1796.   num=ABS(num);
  1797.     
  1798.   buffer[33+sizeof(struct fplStr)]=CHAR_ASCII_ZERO; /* zero byte termination */
  1799.   bufpoint=&buffer[33+sizeof(struct fplStr)]; /* start digit output position */
  1800.     
  1801.   if(num) {
  1802.     while(num>0) {
  1803.       *--bufpoint= digits[num % base];
  1804.       num /= base;
  1805.       len++;
  1806.     }
  1807.     if(is_neg) {
  1808.       *--bufpoint='-';
  1809.       len++;
  1810.     }
  1811.   } else {
  1812.     *--bufpoint=CHAR_ZERO;
  1813.     len++;
  1814.   }
  1815.  
  1816.   GETMEM(*string, len+sizeof(struct fplStr));
  1817.   strcpy((*string)->string, bufpoint);
  1818.   (*string)->len=len;
  1819.   (*string)->alloc=len;
  1820.   return(FPL_OK);
  1821. }
  1822.  
  1823. /**********************************************************************
  1824.  *
  1825.  * fplStrtol()
  1826.  *
  1827.  * Frontend to the FPL Strtol() function to be used by anyone!
  1828.  *
  1829.  ****/
  1830.  
  1831. long PREFIX
  1832. fplStrtol(AREG(0) uchar *string,
  1833.           DREG(0) long base,
  1834.           AREG(1) uchar **end)
  1835. {
  1836.   uchar *dummypoint;
  1837.   if(!end)
  1838.     end = &dummypoint;
  1839.   /*
  1840.    * THIS CAN'T CURRENT CALL DEBUGMAIL SINCE NO struct Data IS USED!!!
  1841.    */
  1842.   return Strtol(string, base, end);
  1843. }
  1844.  
  1845. /**********************************************************************
  1846.  *
  1847.  * Strtol()
  1848.  *
  1849.  * String to long integer. Code copied and changed from the GNU libc
  1850.  * source code package.
  1851.  *
  1852.  ****/
  1853.  
  1854. long REGARGS
  1855. Strtol(uchar *nptr,
  1856.        long base,
  1857.        uchar **end)
  1858. {
  1859.   uchar negative;
  1860.   unsigned long cutoff;
  1861.   unsigned long cutlim;
  1862.   long i;
  1863.   uchar *s;
  1864.   uchar c;
  1865.   uchar *save;
  1866.   long overflow;
  1867.  
  1868.   if (base < 0 || base == 1 || base > 36)
  1869.     base = 10;
  1870.  
  1871.   s = nptr;
  1872.  
  1873.   /* Skip white space.  */
  1874.   while(isspace(*s))
  1875.       s++;
  1876.  
  1877.   if (*s == CHAR_ASCII_ZERO)
  1878.     return (0);
  1879.  
  1880.   /* Check for a sign.  */
  1881.   else if (*s == CHAR_MINUS) {
  1882.     negative = 1;
  1883.     ++s;
  1884.   } else if (*s == CHAR_PLUS) {
  1885.     negative = 0;
  1886.     ++s;
  1887.   } else
  1888.     negative = 0;
  1889.  
  1890.   if ((base == 16 && s[0] == CHAR_ZERO && UPPER(s[1]) == CHAR_UPPER_X) ||
  1891.       (base == 2 && s[0] == CHAR_ZERO && UPPER(s[1]) == CHAR_UPPER_B) )
  1892.     s += 2;
  1893.  
  1894.   /* If BASE is zero, figure it out ourselves.  */
  1895.   if (base == 0)
  1896.     if (*s == '0') {
  1897.       switch(UPPER(s[1])) {
  1898.       case CHAR_UPPER_X:
  1899.     s += 2;
  1900.     base = 16;
  1901.     break;
  1902.       case CHAR_UPPER_B:
  1903.     s += 2;
  1904.     base = 2;
  1905.     break;
  1906.       default:
  1907.     base = 8;
  1908.     break;
  1909.       }
  1910.     } else
  1911.       base = 10;
  1912.  
  1913.   /* Save the pointer so we can check later if anything happened.  */
  1914.   save = s;
  1915.  
  1916.   cutoff = ULONG_MAX / (unsigned long int) base;
  1917.   cutlim = ULONG_MAX % (unsigned long int) base;
  1918.  
  1919.   overflow = 0;
  1920.   i = 0;
  1921.   for (c = *s; c; c = *++s) {
  1922.     if (isdigit(c))
  1923.       c -= '0';
  1924.     else if (isalpha(c))
  1925.       c = UPPER(c) - CHAR_UPPER_A + 10;
  1926.     else
  1927.       break;
  1928.     if (c >= base)
  1929.       break;
  1930.     /* Check for overflow.  */
  1931.     if (i > cutoff || (i == cutoff && c > cutlim))
  1932.       overflow = 1;
  1933.     else {
  1934.       i *= (unsigned long int) base;
  1935.       i += c;
  1936.     }
  1937.   }
  1938.  
  1939.   *end=s; /* this is the end position of the number */
  1940.  
  1941.   /* Check if anything actually happened.  */
  1942.   if (s == save)
  1943.     return (0);
  1944.  
  1945.   /* Check for a value that is within the range of
  1946.      `unsigned long int', but outside the range of `long int'.  */
  1947.   if (i > (negative ?
  1948.        - (unsigned long int) LONG_MIN :
  1949.        (unsigned long int) LONG_MAX))
  1950.     overflow = 1;
  1951.  
  1952.   if (overflow)
  1953.     return negative ? LONG_MIN : LONG_MAX;
  1954.  
  1955.   /* Return the result of the appropriate sign.  */
  1956.   return (negative ? - i : i);
  1957. }
  1958.  
  1959. /*****************************************************************************
  1960.  *
  1961.  * my_memicmp()
  1962.  *
  1963.  * This makes a case insensitive memcmp() with the very same parameters.
  1964.  *
  1965.  *********/
  1966.  
  1967. long REGARGS my_memicmp(uchar *s1, uchar *s2, long len)
  1968. {
  1969.   long pos=0;
  1970.   long result;
  1971.   while(pos < len) {
  1972.     result = tolower(s1[pos]) - tolower(s2[pos]);
  1973.     if(result)
  1974.       return result;
  1975.     pos++;
  1976.   }
  1977.   return 0;
  1978. }
  1979.  
  1980.  
  1981. ReturnCode REGARGS
  1982.   StringExpr(struct Expr *val,        /* original string -> new */
  1983.          struct Data *scr)        /* standard */
  1984. {
  1985.   ReturnCode ret;
  1986.   struct fplStr *whole;
  1987.  
  1988.   CALL(Eat(scr)); /* scan white spaces */
  1989.  
  1990.   if(CHAR_PLUS == scr->text[0]) {
  1991.     
  1992.     GETMEM(whole, sizeof(struct fplStr));
  1993.     memset(whole, 0, sizeof(struct fplStr));
  1994.     
  1995.     /* put string in new string variable */
  1996.     CALL(StrAssign(val->val.str, scr, &whole, 1));
  1997.     
  1998.     do {
  1999.       scr->text++; /* skip the '+' */
  2000.       
  2001.       CALL(Expression(val, scr, CON_STRING, NULL));
  2002.       
  2003.       /* append string to that new variable */
  2004.       CALL(StrAssign(val->val.str, scr, &whole, 1));
  2005.       
  2006.       if(!(val->flags&FPL_NOFREE) && val->val.str)
  2007.     FREE(val->val.str);
  2008.     } while(CHAR_PLUS == scr->text[0]);
  2009.       
  2010.     val->val.str = whole; /* get the string info! */
  2011.     val->flags&=~FPL_NOFREE; /* free this, yes! */
  2012.   }
  2013.   return FPL_OK;
  2014. }
  2015.